﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.Excel;
using Intex.Db.Models;
using Intex.Db.Service;
using System.IO;
using System.Security;
using System.Runtime.InteropServices;
using System.Management;
using System.Net;
using System.Text.RegularExpressions;
using System.Net.Sockets;
using System.Threading;
using PMS.Log;
using System.Diagnostics;

namespace PrintAgentService
{
    public class PrintAgent
    {
        public static string ConfigFilePath = Config.ConfigFilePath;
        public static string TemplateFilePath = Config.TemplateFilePath;
        public static string TempFilePath = Config.TempFilePath;
        public static bool TrialVersion = true;
        public static DateTime ExpiredDate = new DateTime(2020, 11, 30);

        //public static void ResetMode()
        //{
        //    ItemService.ResetMode();
        //}

        public static IEnumerable<string> PrinterArray
        {
            get
            {
                return from string i in System.Drawing.Printing.PrinterSettings.InstalledPrinters select i;
            }
        }

        public void PrintExcel(string fileName, string printerName, int worksheetIndex = 1, int fromPage = 1, int toPage = 1, int copies = 1, bool preview = false)
        {
            if (!System.IO.File.Exists(fileName))
            {
                throw new Exception("文件未找到，" + fileName);
            }
            System.IO.FileInfo f = new System.IO.FileInfo(fileName);
            fileName = f.FullName;
            Application excelApp = new Application();
            var wb = excelApp.Workbooks.Open(fileName);

            try
            {
                var sheet = wb.Worksheets[worksheetIndex] as Worksheet;
                //sheet.PageSetup.Orientation = XlPageOrientation.xlLandscape;
                //sheet.PageSetup.PaperSize = XlPaperSize.xlPaperA5;
                //sheet.PageSetup.PrintArea = "Print_Area";
                sheet.PrintOutEx(fromPage, toPage, copies, preview, printerName);
                wb.Close();

            }
            catch (Exception e)
            {
                wb.Close();
                throw e;
            }
        }

        public System.Data.DataTable GenerateImportData(string fileName, System.Data.DataSet configXml, bool allowBlank = false)
        {
            if (!System.IO.File.Exists(fileName))
            {
                throw new Exception("文件未找到，" + fileName);
            }
            System.IO.FileInfo f = new System.IO.FileInfo(fileName);
            fileName = f.FullName;
            int sheetIndex = 0;
            if (!configXml.Tables.Contains("FileConfig")
                || !configXml.Tables["FileConfig"].Columns.Contains("sheetIndex")
                || configXml.Tables["FileConfig"].Rows.Count <= 0
                || !int.TryParse(configXml.Tables["FileConfig"].Rows[0]["sheetIndex"].ToString(), out sheetIndex))
            {
                throw new Exception("数据导入配置文件数据错误");
            }

            System.Data.DataTable result = null;
            if (!configXml.Tables.Contains("TableColumnConfig")) return result;

            Application excelApp = new Application();
            var wb = excelApp.Workbooks.Open(fileName);
            var sheet = wb.Worksheets[sheetIndex] as Worksheet;

            Dictionary<string, object> singleDataDic = new Dictionary<string, object>();
            Dictionary<string, ExcelColumnAndRowSymbol> tableDataDic = new Dictionary<string, ExcelColumnAndRowSymbol>();
            if (configXml.Tables.Contains("SingleCellConfig"))
            {
                var singleRows = configXml.Tables["SingleCellConfig"].Rows;
                int rowIndex = 0;
                if (singleRows.Count > 0)
                {
                    try
                    {
                        for (int i = 0; i < singleRows.Count; i++)
                        {
                            if (string.IsNullOrEmpty(singleRows[i]["name"].ToString())
                                || string.IsNullOrEmpty(singleRows[i]["col"].ToString())
                                || string.IsNullOrEmpty(singleRows[i]["row"].ToString())
                                || !int.TryParse(singleRows[i]["row"].ToString(), out rowIndex))
                            {
                                continue;
                            }
                            else
                            {
                                var range = (Range)sheet.Cells[rowIndex, singleRows[i]["col"].ToString()];
                                singleDataDic.Add(singleRows[i]["name"].ToString(), range.get_Value());
                            }
                        }
                    }
                    catch
                    {
                        wb.Close();
                        throw new Exception("读取数据导入配置文件错误");
                    }
                }
            }

            if (configXml.Tables.Contains("TableColumnConfig"))
            {
                var tableRows = configXml.Tables["TableColumnConfig"].Rows;
                int rowIndex = 0;
                if (tableRows.Count > 0)
                {
                    try
                    {
                        for (int i = 0; i < tableRows.Count; i++)
                        {
                            if (string.IsNullOrEmpty(tableRows[i]["name"].ToString())
                                || string.IsNullOrEmpty(tableRows[i]["firstCol"].ToString())
                                || string.IsNullOrEmpty(tableRows[i]["firstRow"].ToString())
                                || !int.TryParse(tableRows[i]["firstRow"].ToString(), out rowIndex))
                            {
                                continue;
                            }
                            else
                            {
                                var range = (Range)sheet.Cells[rowIndex, tableRows[i]["firstCol"].ToString()];
                                tableDataDic.Add(tableRows[i]["name"].ToString(), new ExcelColumnAndRowSymbol() { RowIndex = rowIndex, ColumnName = tableRows[i]["firstCol"].ToString() });
                            }
                        }
                    }
                    catch
                    {
                        wb.Close();
                        throw new Exception("读取数据导入配置文件错误");
                    }
                }
            }

            if (tableDataDic.Count > 0)
            {
                if (result == null) result = new System.Data.DataTable("ImportedData");
                foreach (var tdi in tableDataDic)
                {
                    result.Columns.Add(tdi.Key);
                }
                foreach (var sdi in singleDataDic)
                {
                    result.Columns.Add(sdi.Key);
                }
                bool isEnd = false;
                int nullCount = 0;
                while (!isEnd)
                {
                    try
                    {
                        var newRow = result.NewRow();
                        nullCount = 0;
                        foreach (var tdi in tableDataDic)
                        {
                            var range = (Range)sheet.Cells[tdi.Value.RowIndex, tdi.Value.ColumnName];
                            object valueObj = (object)range.get_Value();
                            string value = valueObj == null ? "" : valueObj.ToString();
                            newRow[tdi.Key] = value;
                            tdi.Value.RowIndex++;
                            if (string.IsNullOrEmpty(value))
                            {
                                nullCount++;
                            }
                        }
                        bool condition = allowBlank ? (nullCount >= tableDataDic.Count) : (nullCount > 0);
                        if (condition)  //tableDataDic.Count)
                        {
                            isEnd = true;
                        }
                        else
                        {
                            foreach (var sdi in singleDataDic)
                            {
                                newRow[sdi.Key] = sdi.Value;
                            }
                            result.Rows.Add(newRow);
                        }
                    }
                    catch
                    {
                        wb.Close();
                        throw new Exception("读取数据导入配置文件错误");
                    }
                }
            }


            wb.Close();
            return result;
        }

        public System.Data.DataTable GetDisplayData(out Dictionary<string, ItemType> map, out Dictionary<int, int> orderList, DateTime? filter = null)
        {
            //Config.Create();
            if (filter.HasValue)
            {
                if (filter.Value <= ExpiredDate)
                {
                    ItemService ser = new ItemService();
                    return ser.GetDatas(true, out map, out orderList, null, filter);
                }
                else
                {
                    throw new Exception("测试版软件日期限制");
                }
            }
            else
            {
                ItemService ser = new ItemService();
                return ser.GetDatas(true, out map, out orderList, null, filter);
            }
        }

        public System.Data.DataTable GetPrintData(out Dictionary<string, ItemType> map, out Dictionary<int, int> orderList, List<long> ids)
        {
            //Config.Create();

            ItemService ser = new ItemService();
            return ser.GetDatas(false, out map, out orderList, ids, null);
        }

        public List<string> ImportCheckRepeatedData(System.Data.DataTable data)
        {
            ItemService ser = new ItemService();
            return ser.CheckIndex(data);
        }

        public bool DeleteRepeatedData(List<string> indexs, DateTime date)
        {
            ItemService ser = new ItemService();
            return ser.DeleteItemValues(indexs, date);
        }

        public bool ImportProduceData(System.Data.DataTable data)
        {
            if (TrialVersion
                && data != null
                && data.Rows.Count > 0
                && data.Columns.Contains(Config.CreatedAlias))
            {
                DateTime date = new DateTime();
                if (DateTime.TryParse(data.Rows[0][Config.CreatedAlias].ToString(), out date))
                {
                    if (date <= ExpiredDate)
                    {
                        ItemService ser = new ItemService();
                        return ser.ImportDatas(data);
                    }
                    else
                    {
                        throw new Exception("测试版软件日期限制");
                    }
                }
            }
            return false;
        }

        public bool AppendProduceData(System.Data.DataTable data, string identifyColumnName, IEnumerable<string> redundantDataIds, bool appendRedundant = false)
        {
            ItemService ser = new ItemService();
            if (data != null
                && data.Rows.Count > 0
                && data.Columns.Contains(Config.CreatedAlias)
                && data.Columns.Contains(identifyColumnName))
            {
                DateTime date = new DateTime();
                if (DateTime.TryParse(data.Rows[0][Config.CreatedAlias].ToString(), out date))
                {
                    if (date <= ExpiredDate)
                    {
                        Dictionary<string, ItemType> mapDic = new Dictionary<string, ItemType>();
                        Dictionary<int, int> orderList=new Dictionary<int,int>();
                        var specificDataOnDate = GetDisplayData(out mapDic, out orderList , date);
                        string itemName = ser.GetItemTypeByAlias(identifyColumnName).ItemName;
                        if (specificDataOnDate != null
                            && specificDataOnDate.Rows.Count > 0
                            && specificDataOnDate.Columns.Contains(itemName))
                        {
                            List<string> dataIds, specificDataOnDateIds;
                            dataIds = new List<string>();
                            for (int i = 0; i < data.Rows.Count; i++)
                            {
                                dataIds.Add(data.Rows[i][identifyColumnName].ToString());
                            }
                            specificDataOnDateIds = new List<string>();
                            for (int i = 0; i < specificDataOnDate.Rows.Count; i++)
                            {
                                specificDataOnDateIds.Add(specificDataOnDate.Rows[i][itemName].ToString());
                            }
                            redundantDataIds = specificDataOnDateIds.Where(id => dataIds.Contains(id));
                            if (!appendRedundant && redundantDataIds != null && redundantDataIds.Count() > 0)
                            {
                                return false;
                            }
                        }
                        return ser.ImportDatas(data);
                    }
                    else
                    {
                        throw new Exception("测试版软件日期限制");
                    }
                }
            }
            return false;
        }

        public bool AppendProduceData(System.Data.DataTable data, int index)
        {
            ItemService ser = new ItemService();
            if (data != null
                && data.Rows.Count > 0)
            {
                DateTime date = new DateTime();
                if (DateTime.TryParse(data.Rows[0][Config.CreatedAlias].ToString(), out date))
                {
                    if (date <= ExpiredDate)
                    {
                        return ser.InsertAt(data, index);
                    }
                    else
                    {
                        throw new Exception("测试版软件日期限制");
                    }
                }
            }
            return false;
        }

        public bool SetPrintSymbol(int itemId)
        {
            ItemService ser = new ItemService();
            return ser.SavePrintStatus(itemId);
        }

        public string PrintLabel(string templateFilePath, Dictionary<string, ItemType> mapDic, System.Data.DataRow data, System.Data.DataSet config)
        {
            var configTable = GenerateImportData(templateFilePath, config);
            GC.Collect();
            if (configTable == null)
            {
                throw new Exception("模版配置文件读取失败");
            }
            if (!System.IO.File.Exists(templateFilePath))
            {
                throw new Exception("文件未找到，" + templateFilePath);
            }
            DirectoryInfo tempDir = new DirectoryInfo(PrintAgent.TempFilePath);
            if (!tempDir.Exists)
            {
                tempDir.Create();
            }
            if (!tempDir.Exists)
            {
                throw new Exception("临时文件目录创建异常");
            }
            string newTempName = TempFilePath + Guid.NewGuid().ToString() + ".xlsx";
            if (System.IO.File.Exists(newTempName))
            {
                System.IO.File.Delete(newTempName);
            }
            System.IO.File.Copy(templateFilePath, newTempName);
            if (!System.IO.File.Exists(newTempName))
            {
                throw new Exception("文件未找到，" + newTempName);
            }

            System.IO.FileInfo fi = new System.IO.FileInfo(newTempName);
            Application excelApp = new Application();
            var wb = excelApp.Workbooks.Open(fi.FullName);
            try
            {
                var sheet = wb.Worksheets[1] as Worksheet;
                foreach (var item in mapDic)
                {
                    if (data.Table.Columns.Contains(item.Value.ItemName))
                    {
                        var replaceCellNames = configTable.Select("ReplaceContent='" + item.Key + "'");
                        if (replaceCellNames.Count() > 0)
                        {
                            foreach (var cellData in replaceCellNames)
                            {
                                var range = sheet.get_Range(cellData["CellName"].ToString());
                                if (item.Value.ItemDataType == "joinDataFieldImage"
                                    && System.IO.File.Exists(data[item.Value.ItemName].ToString()))
                                {
                                    if ((bool)range.MergeCells)
                                    {
                                        range = range.MergeArea;
                                    }
                                    sheet.Shapes.AddPicture(data[item.Value.ItemName].ToString(), Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoTrue, range.Left + 10, range.Top + 2, range.Width - 10, range.Height - 2);

                                }
                                else
                                {
                                    range.Value = data[item.Value.ItemName].ToString();
                                }
                            }
                        }
                    }
                }
                wb.Save();
                wb.Close();
                return fi.FullName;
            }
            catch (Exception e)
            {
                wb.Save();
                wb.Close();
                throw e;
            }
        }

        public bool ImportFundamentalData(string fileName, System.Data.DataSet configXml)
        {
            var data = GenerateImportData(fileName, configXml, true);
            GC.Collect();
            ItemService ser = new ItemService();
            return ser.ImportTemplate(data);
        }

        public ItemType GetItemTypeByAlias(string alias)
        {
            ItemService ser = new ItemService();
            return ser.GetItemTypeByAlias(alias);
        }

        public bool SaveItemType(ItemType item)
        {
            ItemService ser = new ItemService();
            return ser.SaveItemType(item);
        }

        public bool ChangeDisplayDataIndex(DateTime date, int sourceIndex, int targetIndex)
        {
            ItemService ser = new ItemService();
            return ser.ChangeIndex(date, sourceIndex, targetIndex);
        }

        public bool DeleteDisplayData(int deleteItemId, int deleteIndex, DateTime date)
        {
            ItemService ser = new ItemService();
            return ser.DeleteItemValues(new List<int>() { deleteItemId }, deleteIndex, date);
        }

        public bool DeleteDisplayData(List<int> itemIds, int deleteIndex, DateTime date)
        {
            ItemService ser = new ItemService();
            return ser.DeleteItemValues(itemIds, deleteIndex, date);
        }

        public bool UpdateItemName(int id, string newName)
        {
            ItemService ser = new ItemService();
            return ser.UpdateItemName(id, newName);
        }

        public System.Data.DataTable GetTemplateValue(string templateName, out Dictionary<string, ItemType> map)
        {
            ItemService ser = new ItemService();
            return ser.GetTemplateValue(templateName, out map);
        }

        public List<string> GetTemplateValues()
        {
            ItemService ser = new ItemService();
            return ser.GetTemplateValues();
        }

        public Dictionary<string, string> GetItemTypes()
        {
            ItemService ser = new ItemService();
            return ser.GetItemTypes();
        }

        public void JoinRowsField(System.Data.DataRow dr, List<ItemType> list, Dictionary<string, string> mapDict)
        {
            ItemService ser = new ItemService();
            ser.JoinRowsField(dr, list, mapDict);
        }

        public class ExcelColumnAndRowSymbol
        {
            public int RowIndex { get; set; }
            public string ColumnName { get; set; }
        }

        public bool SetPrintSymbol(int itemId, string displayString, System.Data.DataTable detailData)
        {
            ItemService ser = new ItemService();
            if (ser.SavePrintStatus(itemId))
            {
                try
                {
                    RecordLog(displayString, detailData);
                }
                catch(Exception e)
                {
                    PMS.Log.Logger.Error(e);
                }
                return true;
            }
            return false;
        }

        private static int _recordTimer = 0;

        public void RecordLog(string displayString, System.Data.DataTable detailData)
        {
            StringBuilder sbData = new StringBuilder();
            sbData.Append("=======================================================\r\n");
            sbData.Append("Display Text:");
            sbData.Append(displayString);
            sbData.Append("\r\n-------------------------------------------------------\r\n");
            if (detailData == null || detailData.Rows.Count < 1)
            {
                sbData.Append("empty");
            }
            else
            {
                int irow = _recordTimer;
                if (_recordTimer >= detailData.Rows.Count)
                {
                    irow = 0;
                }
                for (int icol = 0; icol < detailData.Columns.Count; icol++)
                {
                    sbData.Append(detailData.Rows[irow][icol].ToString() + " ");
                }
            }
            sbData.Append("\r\n");
            sbData.Append("=======================================================");
            if (_recordTimer < detailData.Rows.Count)
            {
                _recordTimer++;
            }
            else
            {
                _recordTimer = 0;
            }
            PMS.Log.Logger.Info(sbData);
        }
    }

    public static class CPrintStatus
    {
        #region API声明

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        internal struct structPrinterDefaults
        {
            [MarshalAs(UnmanagedType.LPTStr)]
            public String pDatatype;
            public IntPtr pDevMode;
            [MarshalAs(UnmanagedType.I4)]
            public int DesiredAccess;
        };

        [DllImport("winspool.Drv", EntryPoint = "OpenPrinter", SetLastError = true,
             CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall),
        SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPTStr)] 
            string printerName,
            out IntPtr phPrinter,
            ref structPrinterDefaults pd);

        [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true,
             CharSet = CharSet.Unicode, ExactSpelling = false,
             CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern bool ClosePrinter(IntPtr phPrinter);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        internal struct structSize
        {
            public Int32 width;
            public Int32 height;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        internal struct structRect
        {
            public Int32 left;
            public Int32 top;
            public Int32 right;
            public Int32 bottom;
        }

        [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
        internal struct FormInfo1
        {
            [FieldOffset(0), MarshalAs(UnmanagedType.I4)]
            public uint Flags;
            [FieldOffset(4), MarshalAs(UnmanagedType.LPWStr)]
            public String pName;
            [FieldOffset(8)]
            public structSize Size;
            [FieldOffset(16)]
            public structRect ImageableArea;
        };

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        internal struct structDevMode
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public String dmDeviceName;
            [MarshalAs(UnmanagedType.U2)]
            public short dmSpecVersion;
            [MarshalAs(UnmanagedType.U2)]
            public short dmDriverVersion;
            [MarshalAs(UnmanagedType.U2)]
            public short dmSize;
            [MarshalAs(UnmanagedType.U2)]
            public short dmDriverExtra;
            [MarshalAs(UnmanagedType.U4)]
            public int dmFields;
            [MarshalAs(UnmanagedType.I2)]
            public short dmOrientation;
            [MarshalAs(UnmanagedType.I2)]
            public short dmPaperSize;
            [MarshalAs(UnmanagedType.I2)]
            public short dmPaperLength;
            [MarshalAs(UnmanagedType.I2)]
            public short dmPaperWidth;
            [MarshalAs(UnmanagedType.I2)]
            public short dmScale;
            [MarshalAs(UnmanagedType.I2)]
            public short dmCopies;
            [MarshalAs(UnmanagedType.I2)]
            public short dmDefaultSource;
            [MarshalAs(UnmanagedType.I2)]
            public short dmPrintQuality;
            [MarshalAs(UnmanagedType.I2)]
            public short dmColor;
            [MarshalAs(UnmanagedType.I2)]
            public short dmDuplex;
            [MarshalAs(UnmanagedType.I2)]
            public short dmYResolution;
            [MarshalAs(UnmanagedType.I2)]
            public short dmTTOption;
            [MarshalAs(UnmanagedType.I2)]
            public short dmCollate;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public String dmFormName;
            [MarshalAs(UnmanagedType.U2)]
            public short dmLogPixels;
            [MarshalAs(UnmanagedType.U4)]
            public int dmBitsPerPel;
            [MarshalAs(UnmanagedType.U4)]
            public int dmPelsWidth;
            [MarshalAs(UnmanagedType.U4)]
            public int dmPelsHeight;
            [MarshalAs(UnmanagedType.U4)]
            public int dmNup;
            [MarshalAs(UnmanagedType.U4)]
            public int dmDisplayFrequency;
            [MarshalAs(UnmanagedType.U4)]
            public int dmICMMethod;
            [MarshalAs(UnmanagedType.U4)]
            public int dmICMIntent;
            [MarshalAs(UnmanagedType.U4)]
            public int dmMediaType;
            [MarshalAs(UnmanagedType.U4)]
            public int dmDitherType;
            [MarshalAs(UnmanagedType.U4)]
            public int dmReserved1;
            [MarshalAs(UnmanagedType.U4)]
            public int dmReserved2;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        internal struct PRINTER_INFO_9
        {
            public IntPtr pDevMode;
        }

        [DllImport("winspool.Drv", EntryPoint = "AddFormW", SetLastError = true,
             CharSet = CharSet.Unicode, ExactSpelling = true,
             CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern bool AddForm(
         IntPtr phPrinter,
            [MarshalAs(UnmanagedType.I4)] int level,
         ref FormInfo1 form);

        [DllImport("winspool.Drv", EntryPoint = "DeleteForm", SetLastError = true,
             CharSet = CharSet.Unicode, ExactSpelling = false, CallingConvention = CallingConvention.StdCall),
        SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern bool DeleteForm(
         IntPtr phPrinter,
            [MarshalAs(UnmanagedType.LPTStr)] string pName);

        [DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false,
             ExactSpelling = true, CallingConvention = CallingConvention.StdCall),
        SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern Int32 GetLastError();

        [DllImport("GDI32.dll", EntryPoint = "CreateDC", SetLastError = true,
             CharSet = CharSet.Unicode, ExactSpelling = false,
             CallingConvention = CallingConvention.StdCall),
        SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern IntPtr CreateDC([MarshalAs(UnmanagedType.LPTStr)] 
            string pDrive,
            [MarshalAs(UnmanagedType.LPTStr)] string pName,
            [MarshalAs(UnmanagedType.LPTStr)] string pOutput,
            ref structDevMode pDevMode);

        [DllImport("GDI32.dll", EntryPoint = "ResetDC", SetLastError = true,
             CharSet = CharSet.Unicode, ExactSpelling = false,
             CallingConvention = CallingConvention.StdCall),
        SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern IntPtr ResetDC(
         IntPtr hDC,
         ref structDevMode
            pDevMode);

        [DllImport("GDI32.dll", EntryPoint = "DeleteDC", SetLastError = true,
             CharSet = CharSet.Unicode, ExactSpelling = false,
             CallingConvention = CallingConvention.StdCall),
        SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern bool DeleteDC(IntPtr hDC);

        [DllImport("winspool.Drv", EntryPoint = "SetPrinterA", SetLastError = true,
            CharSet = CharSet.Auto, ExactSpelling = true,
            CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurityAttribute()]
        internal static extern bool SetPrinter(
           IntPtr hPrinter,
           [MarshalAs(UnmanagedType.I4)] int level,
           IntPtr pPrinter,
           [MarshalAs(UnmanagedType.I4)] int command);

        /* 
         LONG DocumentProperties( 
           HWND hWnd,               // handle to parent window  
           HANDLE hPrinter,         // handle to printer object 
           LPTSTR pDeviceName,      // device name 
           PDEVMODE pDevModeOutput, // modified device mode 
           PDEVMODE pDevModeInput,  // original device mode 
           DWORD fMode              // mode options 
           ); 
         */
        [DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesA", SetLastError = true,
        ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        internal static extern int DocumentProperties(
           IntPtr hwnd,
           IntPtr hPrinter,
           [MarshalAs(UnmanagedType.LPStr)] string pDeviceName,
           IntPtr pDevModeOutput,
           IntPtr pDevModeInput,
           int fMode
           );

        [DllImport("winspool.Drv", EntryPoint = "GetPrinterA", SetLastError = true,
        ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        internal static extern bool GetPrinter(
           IntPtr hPrinter,
           int dwLevel,
           IntPtr pPrinter,
           int dwBuf,
           out int dwNeeded
           );

        [Flags]
        internal enum SendMessageTimeoutFlags : uint
        {
            SMTO_NORMAL = 0x0000,
            SMTO_BLOCK = 0x0001,
            SMTO_ABORTIFHUNG = 0x0002,
            SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
        }
        const int WM_SETTINGCHANGE = 0x001A;
        const int HWND_BROADCAST = 0xffff;

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern IntPtr SendMessageTimeout(
           IntPtr windowHandle,
           uint Msg,
           IntPtr wParam,
           IntPtr lParam,
           SendMessageTimeoutFlags flags,
           uint timeout,
           out IntPtr result
           );

        //EnumPrinters用到的函数和机构体 
        [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool EnumPrinters(PrinterEnumFlags Flags, string Name, uint Level,
            IntPtr pPrinterEnum, uint cbBuf,
        ref uint pcbNeeded, ref uint pcReturned);

        [StructLayout(LayoutKind.Sequential)]
        internal struct PRINTER_INFO_2
        {
            public string pServerName;
            public string pPrinterName;
            public string pShareName;
            public string pPortName;
            public string pDriverName;
            public string pComment;
            public string pLocation;
            public IntPtr pDevMode;
            public string pSepFile;
            public string pPrintProcessor;
            public string pDatatype;
            public string pParameters;
            public IntPtr pSecurityDescriptor;
            public uint Attributes;
            public uint Priority;
            public uint DefaultPriority;
            public uint StartTime;
            public uint UntilTime;
            public uint Status;
            public uint cJobs;
            public uint AveragePPM;
        }

        [FlagsAttribute]
        internal enum PrinterEnumFlags
        {
            PRINTER_ENUM_DEFAULT = 0x00000001,
            PRINTER_ENUM_LOCAL = 0x00000002,
            PRINTER_ENUM_CONNECTIONS = 0x00000004,
            PRINTER_ENUM_FAVORITE = 0x00000004,
            PRINTER_ENUM_NAME = 0x00000008,
            PRINTER_ENUM_REMOTE = 0x00000010,
            PRINTER_ENUM_SHARED = 0x00000020,
            PRINTER_ENUM_NETWORK = 0x00000040,
            PRINTER_ENUM_EXPAND = 0x00004000,
            PRINTER_ENUM_CONTAINER = 0x00008000,
            PRINTER_ENUM_ICONMASK = 0x00ff0000,
            PRINTER_ENUM_ICON1 = 0x00010000,
            PRINTER_ENUM_ICON2 = 0x00020000,
            PRINTER_ENUM_ICON3 = 0x00040000,
            PRINTER_ENUM_ICON4 = 0x00080000,
            PRINTER_ENUM_ICON5 = 0x00100000,
            PRINTER_ENUM_ICON6 = 0x00200000,
            PRINTER_ENUM_ICON7 = 0x00400000,
            PRINTER_ENUM_ICON8 = 0x00800000,
            PRINTER_ENUM_HIDE = 0x01000000
        }

        //打印机状态 
        [FlagsAttribute]
        internal enum PrinterStatus
        {
            PRINTER_STATUS_BUSY = 0x00000200,
            PRINTER_STATUS_DOOR_OPEN = 0x00400000,
            PRINTER_STATUS_ERROR = 0x00000002,
            PRINTER_STATUS_INITIALIZING = 0x00008000,
            PRINTER_STATUS_IO_ACTIVE = 0x00000100,
            PRINTER_STATUS_MANUAL_FEED = 0x00000020,
            PRINTER_STATUS_NO_TONER = 0x00040000,
            PRINTER_STATUS_NOT_AVAILABLE = 0x00001000,
            PRINTER_STATUS_OFFLINE = 0x00000080,
            PRINTER_STATUS_OUT_OF_MEMORY = 0x00200000,
            PRINTER_STATUS_OUTPUT_BIN_FULL = 0x00000800,
            PRINTER_STATUS_PAGE_PUNT = 0x00080000,
            PRINTER_STATUS_PAPER_JAM = 0x00000008,
            PRINTER_STATUS_PAPER_OUT = 0x00000010,
            PRINTER_STATUS_PAPER_PROBLEM = 0x00000040,
            PRINTER_STATUS_PAUSED = 0x00000001,
            PRINTER_STATUS_PENDING_DELETION = 0x00000004,
            PRINTER_STATUS_PRINTING = 0x00000400,
            PRINTER_STATUS_PROCESSING = 0x00004000,
            PRINTER_STATUS_TONER_LOW = 0x00020000,
            PRINTER_STATUS_USER_INTERVENTION = 0x00100000,
            PRINTER_STATUS_WAITING = 0x20000000,
            PRINTER_STATUS_WARMING_UP = 0x00010000
        }

        //GetDefaultPrinter用到的API函数说明 
        [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern bool GetDefaultPrinter(StringBuilder pszBuffer, ref int size);

        //SetDefaultPrinter用到的API函数声明 
        [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern bool SetDefaultPrinter(string Name);

        //EnumFormsA用到的函数声明，应该和EnumPrinters类似 
        [DllImport("winspool.drv", EntryPoint = "EnumForms")]
        internal static extern int EnumFormsA(IntPtr hPrinter, int Level, ref byte pForm, int cbBuf, ref int pcbNeeded, ref int pcReturned);



        #endregion

        static byte[] sendData = new byte[5];


        static CPrintStatus()
        {
            sendData[0] = 27;
            sendData[1] = 87;
            sendData[2] = 83;
            sendData[3] = 10;
            sendData[4] = 0;
        }

        internal static int GetPrinterStatusInt(string PrinterName)
        {
            int intRet = 0;
            IntPtr hPrinter;
            structPrinterDefaults defaults = new structPrinterDefaults();

            if (OpenPrinter(PrinterName, out hPrinter, ref defaults))
            {
                int cbNeeded = 0;
                bool bolRet = GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out cbNeeded);
                if (cbNeeded > 0)
                {
                    IntPtr pAddr = Marshal.AllocHGlobal((int)cbNeeded);
                    bolRet = GetPrinter(hPrinter, 2, pAddr, cbNeeded, out cbNeeded);
                    if (bolRet)
                    {
                        PRINTER_INFO_2 Info2 = new PRINTER_INFO_2();

                        Info2 = (PRINTER_INFO_2)Marshal.PtrToStructure(pAddr, typeof(PRINTER_INFO_2));

                        intRet = System.Convert.ToInt32(Info2.Status);
                    }
                    Marshal.FreeHGlobal(pAddr);
                }
                ClosePrinter(hPrinter);
            }

            return intRet;
        }

        private static List<IPEndPoint> _ipAddressList = new List<IPEndPoint>();

        private static List<IPEndPoint> GetPrintIpAddress(List<string> printerName)
        {
            try
            {
                if (printerName == null || !printerName.Any()) return new List<IPEndPoint>();
                //if (_ipAddressList != null && _ipAddressList.Any()) return _ipAddressList;
                _ipAddressList = refreshIPList(printerName);
                return _ipAddressList;
            }
            catch (Exception e)
            {
                PMS.Log.Logger.Error(e);
                return null;
            }
        }

        public static void RefreshPrinter(List<string> printName)
        {
            _ipAddressList = refreshIPList(printName);
        }

        private static List<IPEndPoint> refreshIPList(List<string> printerName)
        {
            if (Config.IsDebug) return new List<IPEndPoint>();
            //Logger.Info("test");
            string query = "";
            Dictionary<string, string> printerMap = new Dictionary<string, string>();
            Dictionary<string, IPEndPoint> ipMap = new Dictionary<string, IPEndPoint>();
            foreach (var name in printerName)
            {
                query += string.Format("or Name = '{0}' ", name);
            }
            query = query.Substring(2);
            if (query.Length > 0) query = " where " + query;
            query = "select * from Win32_Printer " + query;
            var search = new ManagementObjectSearcher(query);
            var printers = search.Get();
            query = "";
            foreach (var printer in printers)
            {
                query += string.Format("or Name='{0}' ", printer.Properties["PortName"].Value);
                printerMap.Add(printer.Properties["Name"].Value.ToString(), printer.Properties["PortName"].Value.ToString());
            }
            query = query.Substring(2);
            if (query.Length > 0) query = " where " + query;
            query = "select * from Win32_TCPIPPrinterPort" + query;
            search = new ManagementObjectSearcher(query);
            var ips = search.Get();
            List<IPEndPoint> address = new List<IPEndPoint>();
            foreach (var ip in ips)
            {
                var ipa = IPAddress.Parse(ip.Properties["HostAddress"].Value.ToString());
                var portNumber = int.Parse(ip.Properties["PortNumber"].Value.ToString());
                ipMap.Add(ip.Properties["Name"].Value.ToString(), new IPEndPoint(ipa, portNumber));
            }
            foreach (var item in printerName)
            {
                if (ipMap.ContainsKey(printerMap[item]))
                    address.Add(ipMap[printerMap[item]]);
            }
            return address;
        }

        /// <summary> 
        /// 获取当前指定打印机的状态 
        /// </summary> 
        /// <param name="PrinterName">打印机名称</param> 
        /// <returns>打印机状态描述</returns> 
        public static bool IsPrinterReady(string PrinterName, int timeOut = 3000)
        {
            if (Config.IsDebug)
            {
                return DebugCheckPrinter();
            }
            var iplist = GetPrintIpAddress(new List<string>() { PrinterName });
            if (iplist != null && iplist.Count > 0)
            {
                TcpClient tc = new TcpClient();
                try
                {
                    tc.Connect(iplist[0]);
                    var sendStream = tc.GetStream();
                    sendStream.Write(sendData, 0, 5);
                    int readbytecount = 0;
                    Thread.Sleep(timeOut);
                    byte[] receiveBuffer = new byte[1024];
                    readbytecount = sendStream.Read(receiveBuffer, 0, 1024);
                    if (readbytecount == 13)
                    {
                        if (receiveBuffer[2] == 48 && receiveBuffer[3] == 48)
                        {
                            return true;
                        }
                    }
                    sendStream.Close();
                    tc.Close();
                }
                catch (Exception e)
                {
                    PMS.Log.Logger.Error(e);
                }
            }
            return false;
        }



        private static Random rnd = new Random();

        private static int _debugCheckPrinterNum = -1;
        private static int _ExceptionTimers = 0;

        private static bool DebugCheckPrinter()
        {
            if (_ExceptionTimers > 0)
            {
                _ExceptionTimers--;
                Debug.WriteLine(_ExceptionTimers);
                return false;
            }
            int checkNum = rnd.Next(100);
            if(!( checkNum > _debugCheckPrinterNum))
            {
                _ExceptionTimers = 500;
                //throw new Exception();
                _debugCheckPrinterNum = 0;
                return false;
            }
            return true;
        }

    }
}
